/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import tal.drivechain.system.Globals;
import tal.drivechain.system.Splasher;

/**
 *
 * @author Main
 */
public class GUIWindow extends JFrame implements ContainerListener, WindowFocusListener, WindowListener {
    private Color color, darker;
    private Container container;
    private JPanel containerPanel;
    private TitleBar titleBar;
    private WindowResizer resizer;
    private MenuPanel menuPanel;
    private PopupFrame popupFrame;
    private JPanel content;
    private Font font;

    private boolean minimized = false;
    private boolean decorate, resizable, exitOnHide;
    private boolean anchorMenu;
    private boolean menuBarVisible = false;
    private String aboutText;

    public GUIWindow(boolean anchor) {
        this("", true, true, anchor);
    }

    public GUIWindow(String title, boolean decorate, boolean resizable, boolean anchorMenu) {
        super(title);
        this.resizable = resizable;
        this.decorate = decorate;
        this.anchorMenu = anchorMenu;

        setLayout(new GridBagLayout());
        setUndecorated(true);
        setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        setBackground(Color.BLACK);
        this.setIconImage(Globals.getChainIcon());
    }

    public void setAnchorMenu(boolean anchorMenu) {
        this.anchorMenu = anchorMenu;
        if (anchorMenu) {
            if (popupFrame!=null) popupFrame.setVisible(false);
            if (menuPanel!=null) menuPanel.setVisible(true);
            menuBarVisible = true;
            if (content!=null) content.add(menuPanel, BorderLayout.WEST);
            if (popupFrame!=null)
                popupFrame.remove(menuPanel);
            validate();
        } else {
            if (menuPanel!=null) menuPanel.setVisible(true);
            menuBarVisible = false;
            if (popupFrame==null) {
                createPopupFrame();
                if (menuPanel!=null) popupFrame.add(menuPanel);
                popupFrame.pack();
            }
            if (content!=null) content.remove(menuPanel);
            validate();
        }
        if (menuPanel!=null) menuPanel.updateAnchor();
    } public boolean isMenuAnchored() { return anchorMenu; }

    public void setColor(Color color) {
        if (color==null) return;
        this.color = color;
        this.darker = color.darker();
        if (titleBar!=null) titleBar.updateColors();
        if (menuPanel!=null) menuPanel.updateColors();
        if (content!=null) content.setBorder(BorderFactory.createLineBorder(color, 1));
        if (resizer!=null) resizer.setColor(color);
    } public Color getColor() { return color; }

    @Override
    public void setFont(Font font) {
        if (font==null) return;
        
        super.setFont(font);
        this.font = font;
        if (titleBar!=null)
            titleBar.updateFonts();
        if (menuPanel!=null)
            menuPanel.updateFonts();
    }

    public void setDecorate(boolean decorate) {
        this.decorate = decorate;
        if (content!=null) {
            if (decorate) {
                titleBar.setVisible(true);
                menuPanel.setVisible(true);
                if (anchorMenu) menuBarVisible = true;
            } else {
                titleBar.setVisible(false);
                menuPanel.setVisible(false);
                menuBarVisible = false;
            }
        }
    } public boolean isDecorated() { return decorate; }

    public void setExitOnHide(boolean exitOnHide) {
        this.exitOnHide = exitOnHide;
        if (menuPanel!=null)
            menuPanel.basicMenu.closeBtn.setText((exitOnHide?"quit":"close"));
    } public boolean isExitOnHide() { return exitOnHide; }

    public void setMenuBarVisible(Boolean visible) {
        menuBarVisible = visible;
        if (anchorMenu) {
            menuPanel.setVisible(visible);
            validate();
        } 
    } public boolean isMenuBarVisible() {
        return menuBarVisible;
    }
    
    @Override
    public void setTitle(String title) {
        super.setTitle(title);
        if (titleBar!=null)
            titleBar.setTitle(title);
    }

    @Override
    public void setResizable(boolean resizable) {
        this.resizable = resizable;
        if (resizer!=null) {
            resizer.getParent().setVisible(resizable);
            this.pack();
        }
    }

    @Override
    public boolean isResizable() {
        return resizable;
    }
    public Container getModuleContainer() {
        return container;
    }
    public Container getMenuContainer() { return menuPanel.customMenuPanel; }

    public void initWindow() {
        container = new JPanel(new GridBagLayout());
        container.setBackground(Color.BLACK);
        menuPanel = new MenuPanel();
        titleBar = new TitleBar();
        if (!anchorMenu) createPopupFrame();
        GridBagConstraints c = new GridBagConstraints();
        c.gridy = 0;
        c.weightx = 1.0;
        c.fill = GridBagConstraints.HORIZONTAL;
        add(titleBar, c);
        containerPanel = new JPanel();
        containerPanel.setBackground(Color.BLACK);
        containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS));
        containerPanel.add(container);
        if (resizable) {
            JPanel resizePanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
            resizer = new WindowResizer(this, color);
            resizePanel.add(resizer);
            containerPanel.add(resizePanel);
        }
        content = new JPanel();
        content.setLayout(new BorderLayout());
        content.setBackground(Color.BLACK);
        content.setBorder(BorderFactory.createLineBorder(color, 1));
        if (anchorMenu) {
            content.add(menuPanel, BorderLayout.WEST);
            menuBarVisible = true;
        }
        content.add(containerPanel, BorderLayout.CENTER);
        c.gridy = 1;
        c.fill = GridBagConstraints.BOTH;
        c.weighty = 1.0;
        add(content, c);
        setDecorate(decorate);
        pack();

        container.addContainerListener(this);
        this.addWindowFocusListener(this);
        this.addWindowListener(this);
    }

    private void createPopupFrame() {
        popupFrame = new PopupFrame(this);
        popupFrame.setUndecorated(true);
        if (menuPanel!=null) popupFrame.add(menuPanel);
        popupFrame.setVisible(false);
    }

    private void showAbout() {
        new Splasher().showAbout(aboutText);
    }

    public void setAboutText(String text) {
        aboutText = text;
    } public String getAboutText() { return aboutText; }

    protected void closeButtonClick() {
        WindowEvent e = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
        this.processWindowEvent(e);
        for (WindowListener l : this.getWindowListeners()) {
            l.windowClosing(e);
        }
    }

    protected void menuButtonClick() {
        if (anchorMenu) {
            menuPanel.setVisible(!menuPanel.isVisible());
            menuBarVisible = menuPanel.isVisible();
            validate();
        } else {
            menuPanel.setVisible(true);
            if (popupFrame==null) createPopupFrame();
            if (popupFrame.isVisible()) {
                popupFrame.setVisible(false);
            }
            GUIButton button = titleBar.menuButton;
            popupFrame.setLocation(button.getLocationOnScreen().x, button.getLocationOnScreen().y+button.getHeight());
            popupFrame.setVisible(true);

        }
    }

    class MenuPanel extends JPanel {
        JPanel customMenuPanel;
        BasicMenu basicMenu;

        public MenuPanel() {
            super();
            setLayout(new GridBagLayout());
            customMenuPanel = new JPanel(new GridBagLayout());
            customMenuPanel.addContainerListener(GUIWindow.this);
            basicMenu = new BasicMenu();
            setBackground(Color.BLACK);
            setColor(color);
            setBorder(BorderFactory.createMatteBorder(0, 1, 0, 1, color));
            
            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 0;
            c.fill = GridBagConstraints.BOTH;
            add(customMenuPanel, c);

            c.gridy = 1;
            c.weighty = 1.0;
            add(new JPanel(), c);

            c = new GridBagConstraints();
            c.gridy = 2;
            c.fill = GridBagConstraints.BOTH;
            add(basicMenu, c);
        }

        public void updateColors() {
            basicMenu.updateColors();
            updateAnchor();
        }

        public void updateFonts() {
            basicMenu.updateFonts();
            menuPanel.validate();
            if (popupFrame!=null) {
                popupFrame.pack();
            }
        }

        public void updateAnchor() {
            if (anchorMenu) {
                setBorder(BorderFactory.createMatteBorder(0, 1, 0, 1, color));
            }
            else {
                setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, color));
            }
        }
    }

    class BasicMenu extends JPanel {
        TextButton aboutBtn;
        TextButton closeBtn;
        
        public BasicMenu() {
            super();
            setLayout(new GridBagLayout());
            aboutBtn = new TextButton("about...", color, font);
            aboutBtn.setHasBorder(false);
            closeBtn = new TextButton((exitOnHide?"quit":"close"), color, font);
            closeBtn.setHasBorder(false);
            aboutBtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    showAbout();
                }
            });
            closeBtn.addActionListener(new ActionListener() {            
                public void actionPerformed(ActionEvent e) {
                    closeButtonClick();
                }
            });

            GridBagConstraints c = new GridBagConstraints();
            c.gridy = 0;
            c.fill = GridBagConstraints.HORIZONTAL;
            c.ipadx = 2;
            add(aboutBtn, c);
            c.gridy = 1;
            add(closeBtn, c);            
        }
        
        public void updateColors() {
            aboutBtn.setColor(color);
            closeBtn.setColor(color);
        }

        public void updateFonts() {
            if (font==null) return;
            
            setFont(font);
            aboutBtn.setFont(font);
            aboutBtn.setPreferredSize(aboutBtn.calculateSize());
            closeBtn.setFont(font);
            closeBtn.setPreferredSize(closeBtn.calculateSize());
        }
    }

    class TitleBar extends JPanel {
        DragLabel titleLabel;
        GUIButton menuButton, minimizeButton, hideButton;

        public TitleBar() {
            super();
            setBackground(Color.BLACK);
            titleLabel = new DragLabel(getTitle() + "  ", GUIWindow.this);
            titleLabel.setOpaque(true);
            titleLabel.setBackground(color);
            titleLabel.setFont(font);
            titleLabel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (e.getClickCount()==2) {
                        if (minimized) {
                            containerPanel.setVisible(true);
                            minimized = false;
                        } else { containerPanel.setVisible(false); minimized = true; }
                        pack();
                    }
                }
            });

            menuButton = new GUIButton(color) {
                @Override
                protected void drawImage(Graphics g) {
                    g.fillRect(this.getWidth()/2-4, this.getHeight()/2-4, 8, 8);
                }

                @Override
                protected Dimension calculateSize() {
                    return new Dimension(16,16);
                }
            };

            menuButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) { menuButtonClick(); }
            });

            menuButton.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (e.getButton()==MouseEvent.BUTTON3) {
                        // anchor or unanchor the menu temporarily.
                        setAnchorMenu(!anchorMenu);
                    }
                }
            });

            minimizeButton = new GUIButton(color) {
                @Override
                protected void drawImage(Graphics g) {
                    g.fillPolygon(new int[] { 5, getWidth()-5, getWidth()/2},
                            new int[] {6, 6, getHeight()-5}, 3);
                }

                @Override
                protected Dimension calculateSize() {
                    return new Dimension(16, 16);
                }
            };

            minimizeButton.setNegative(true);
            minimizeButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {  setExtendedState(JFrame.ICONIFIED); }
            });

            hideButton = new GUIButton(color) {
                @Override
                protected void drawImage(Graphics g) {
                    g.drawLine(6, 6, getWidth()-6, getHeight()-6);
                    g.drawLine(getWidth()-6, 6, 6, getHeight()-6);
                }

                @Override
                protected Dimension calculateSize() {
                    return new Dimension(16,16);
                }
            };
            hideButton.setNegative(true);
            hideButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) { closeButtonClick(); }
            });

            setLayout(new GridBagLayout());
            GridBagConstraints c = new GridBagConstraints();

            c.anchor = GridBagConstraints.PAGE_START;
            c.gridx = 0;
            add(menuButton, c);

            c = new GridBagConstraints();
            c.gridx = 1;
            c.fill = GridBagConstraints.VERTICAL;
            add(titleLabel, c);

            c.gridx = 2;
            c.fill = GridBagConstraints.BOTH;
            c.weightx = 1.0;
            JPanel box = new JPanel();
            box.setBackground(Color.BLACK);
            add(box, c);

            c = new GridBagConstraints();
            c.gridx = 3;
            add(minimizeButton, c);

            c.gridx = 4;
            c.anchor = GridBagConstraints.PAGE_END;
            add(hideButton);
        }

        private void gainedFocus() {
            titleLabel.setBackground(color);
            menuButton.setColor(color);
        }

        private void lostFocus() {
            titleLabel.setBackground(darker);
            menuButton.setColor(darker);
        }

        private void setTitle(String title) {
            titleLabel.setText(title + "  ");
        }

        private void updateColors() {
            if (GUIWindow.this.hasFocus()) {
                gainedFocus();
            } else lostFocus();

            minimizeButton.setColor(color);
            hideButton.setColor(color);
        }

        private void updateFonts() {
            titleLabel.setFont(font);
            menuButton.setFont(font);
            minimizeButton.setFont(font);
            hideButton.setFont(font);
        }
    }

    public void componentAdded(ContainerEvent e) {
        pack();
    }

    public void windowGainedFocus(WindowEvent e) {
        if (titleBar!=null)
            titleBar.gainedFocus();
    }

    public void windowLostFocus(WindowEvent e) {
        if (e.getOppositeWindow()!=popupFrame) {
            titleBar.lostFocus();
        }
    }

    public void windowClosing(WindowEvent e) {
        if (popupFrame!=null)
            popupFrame.setVisible(false);
    }

    public void windowClosed(WindowEvent e) {
        if (popupFrame!=null)
            popupFrame.setVisible(false);
    }

    public void windowIconified(WindowEvent e) {
        if (popupFrame!=null)
            popupFrame.setVisible(false);
    }

    public void windowDeiconified(WindowEvent e) {
        if (popupFrame!=null)
            popupFrame.setVisible(false);
    }

    public void windowActivated(WindowEvent e) { }

    public void windowDeactivated(WindowEvent e) { }

    public void windowOpened(WindowEvent e) { }

    public void componentRemoved(ContainerEvent e) { }

}
class PopupFrame extends JFrame implements WindowFocusListener, ContainerListener, MouseListener, ComponentListener {
    private boolean armed = false;

    public PopupFrame(Window parent) {
        super();
        this.setUndecorated(true);
        this.setBackground(Color.BLACK);
        this.setAlwaysOnTop(true);
        this.addWindowFocusListener(this);
        this.addContainerListener(this);
        this.addComponentListener(this);
    }

    public void mousePressed(MouseEvent e) {
        armed = true;
    }

    public void mouseReleased(MouseEvent e) {
        if (armed) {
            armed = false;
            setVisible(false);
        }
    }

    public void windowLostFocus(WindowEvent e) {
        this.setVisible(false);
        armed = false;
    }

    public void mouseExited(MouseEvent e) {
        armed = false;
    }
    
    public void windowGainedFocus(WindowEvent e) { }

    public void componentAdded(ContainerEvent e) { pack(); }

    public void componentRemoved(ContainerEvent e) { }

    public void mouseClicked(MouseEvent e) { }

    public void mouseEntered(MouseEvent e) { }

    public void componentResized(ComponentEvent e) {
        pack();
    }

    public void componentMoved(ComponentEvent e) {
    }

    public void componentShown(ComponentEvent e) {
        pack();
    }

    public void componentHidden(ComponentEvent e) {
        pack();
    }
}

class DragLabel extends JLabel implements MouseMotionListener, MouseListener {

    JFrame frame;
    int xDiff = 0, yDiff = 0;

    public DragLabel(String label, JFrame frame) {
        super(label);
        this.frame = frame;
        this.addMouseMotionListener(this);
        this.addMouseListener(this);
    }

    public void mouseDragged(MouseEvent e) {
        frame.setLocation(frame.getLocation().x + e.getX() - xDiff, frame.getLocation().y + e.getY() - yDiff);
    }

    public void mouseMoved(MouseEvent e) { }

    public void mouseClicked(MouseEvent e) { }

    public void mousePressed(MouseEvent e) {
        xDiff = e.getX();
        yDiff = e.getY();
    }

    public void mouseReleased(MouseEvent e) { }

    public void mouseEntered(MouseEvent e) { }

    public void mouseExited(MouseEvent e) { }

}